/*

$Id: nvcdriver.c 6 2009-10-15 06:30:15Z alex.mobilebi $
$Date: 2009-10-15 06:30:15 +0000 (Thu, 15 Oct 2009) $
$Author: alex.mobilebi $
$Revision: 6 $

Copyright(C) 2009 Alex Lee(alex@mobilebi.com)

*/


#include "nvcdriver.h"

#define DRV_NAME             "NV2009"
#define DFX DRV_NAME         ":"
#define DRV_VERSION          "Version:0.1"
#define DRV_DESCRIPTION      "MobileBI(R) NewView E1 Card Driver"
#define DRV_COPYRIGHT        "Copyright(c) 2009 MobileBI Inc."
#define DRV_RELDATE          "May 1, 2009"

MODULE_AUTHOR(DRV_COPYRIGHT);
MODULE_DESCRIPTION(DRV_DESCRIPTION);
MODULE_LICENSE("Dual BSD/GPL");
MODULE_VERSION(DRV_VERSION);

static int debug = NV_MSG_OPEN|NV_MSG_RELEASE;
static int nmajor = 0;
static unsigned char * packet_buffer[BUF_PACKET_NUM];
static unsigned int buf_windex = 0;
static unsigned int buf_rindex = 0;
static char is_buffer_init = 0;
static struct workqueue_struct * nv_workqueue = NULL;
static unsigned char packet_status_type[8] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
module_param(debug, int, 0);
MODULE_PARM_DESC(debug, "Debug level (0=none,..., 16=all)");

static struct pci_device_id nv_pci_tbl[] =
{
	{PCI_DEVICE(PCI_VENDOR_ID_DALLAS,PCI_DEVICE_ID_DS31256)},
	{0,},
};
MODULE_DEVICE_TABLE(pci, nv_pci_tbl);

static struct file_operations nv_fops =
{
	.owner = THIS_MODULE,
	.read = nv_read,
	.write = nv_write,
	.ioctl = nv_ioctl,
	.open = nv_open,
	.release = nv_release
};

static struct pci_driver nv_pci_driver =
{
	.name      = DRV_NAME,
	.id_table  = nv_pci_tbl,
	.probe     = nv_probe,
	.remove    = __devexit_p(nv_remove),
#ifdef CONFIG_PM
	.suspend   = nv_suspend,
	.resume    = nv_resume
#endif
};

/**************************************************************************
*                                                                        *
*                functions for file_operations                           *
*                                                                        *
**************************************************************************/

/*
* nv_read - put rx dma data to user data buffer
* @filp - file pointer points to the device file whose data should be read
* @buff - user data buffer address where the dma data placed
* @count - user data buffer size
* @offp - device file position offset pointer,ignored in this implements
*
* Returns - bytes count has read
*/
static ssize_t nv_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
{
	int err;
	int cnt_to_read;
	unsigned short read_size;
	int windex = buf_windex;
	int rindex = buf_rindex;
	nvDevicePtr ndev = NULL;
	ndev = filp->private_data;
	if(ndev == NULL || PCI_FUNC(ndev->devfn) == DS31256_BRIDGE_FN)/*对本地总线的访问由ioctl函数负责*/
		return -EINVAL;

	cnt_to_read = get_buf_cnt_to_read(rindex, windex);
		
	if(cnt_to_read > 0)
	{
		read_size = ((packet_buffer[rindex][OFF_LENGTH_HIGH] << 8) | packet_buffer[rindex][OFF_LENGTH_LOW]) + 2;
		//if(debug & NV_MSG_READ)
			//printk(DFX"device %d read a packet len = %d, index[r %d, w %d]\n", MINOR(ndev->dev_id), read_size, rindex, windex);
		if(read_size > count)
			return -ENOBUF;
		if((read_size <= BUF_PACKET_SIZE) && (err = copy_to_user(buff, packet_buffer[rindex], read_size)))
			return err;
		
		set_rw_pos(&buf_rindex, 1);
		
		return read_size;
	}
	return 0;
}

/*
* nv_write - place user data in the dma buffer
* @filp - file pointer points to the device file whose dma will be placed user data
* @buff - user data buffer address 
* @count - user data buffer size
* @offp - device file position offset pointer,ignored in this implements
*/
static ssize_t nv_write(struct file *filp, const char __user *buff, size_t count, loff_t *offp)
{
	nvDevicePtr ndev = NULL;
	ndev = (nvDevicePtr)filp->private_data;
	if(ndev == NULL || PCI_FUNC(ndev->devfn) == DS31256_BRIDGE_FN)
		return -EINVAL;

	return write_to_dma(ndev, buff, count);
}

/*
* nv_open - open the specific device file and allocate resource for subsequent operations
* @node - device node in system
* @filp - device file pointer which file will be opened
*
* Returns - 0 : normal
*/
static int nv_open(struct inode *node, struct file *filp)
{
	nvDevicePtr ndev = NULL;
	if(filp->private_data)//device file has been opened
	{
		if(debug & NV_MSG_OPEN)
			printk(DFX"device file has been opened, nv_open returns -EMFILE.\n");
		return -EMFILE;
	}

	ndev = container_of(node->i_cdev, nvDevice, device);
	if(ndev == NULL)
	{
		printk(DFX"can't get nv_device from data structure, nv_open returns -ENODEV.\n");
		return -ENODEV;
	}
	if(PCI_FUNC(ndev->devfn) == DS31256_PCI_FN)
	{
		if(ndev->operation & OPERATION_RX)
		{
			ndev->rdma = alloc_rx_dma();
			if(ndev->rdma == NULL)
			{
				printk(DFX"[device %d]can't alloce memory for Rx dma, nv_open returns -ENOMEM.\n", MINOR(ndev->dev_id));
				return -ENOMEM;
			}

		}
		if(ndev->operation & OPERATION_TX)
		{
			ndev->tdma = alloc_tx_dma();
			if(ndev->tdma == NULL)
			{
				printk(DFX"[device %d]can't alloce memory for Tx dma, nv_open returns -ENOMEM.\n", MINOR(ndev->dev_id));
				return -ENOMEM;
			}

		}			
	}
	if(request_irq(ndev->irq, (irq_handler_t)nv_interrupt, IRQF_SHARED, "nvc", (void *)ndev))
	{
		printk(DFX"[device %d]can't get assigned irq %i\n", MINOR(ndev->dev_id), ndev->irq);
		ndev->irq = -1;
	}
	filp->private_data = ndev;//set opened flag
	INIT_WORK(&ndev->my_work, nv_do_work);//
	if(!is_buffer_init)
	{
		buf_windex = 0;
		buf_rindex = 0;
		is_buffer_init = 1;
	}
	if(debug & NV_MSG_OPEN)
		printk(DFX"[device %d]device file opened.\n", MINOR(ndev->dev_id));

	return 0;
}

/*
* nv_release - close the specific device file and release the resource
* @node - device node in system
* @filp - device file pointer which file will be closed
*
*/
static int nv_release(struct inode *node, struct file *filp)
{
	nvDevicePtr ndev = NULL;	
	if(filp->private_data == NULL)//device has been closed
	{
		if(debug & NV_MSG_RELEASE)
			printk(DFX"device file has been closed or not yet opened, nv_release returns nothing.\n");
		return 0;
	}
	ndev = container_of(node->i_cdev, nvDevice, device);
	if(ndev == NULL)
	{
		if(debug & NV_MSG_RELEASE)
			printk(DFX"can't get nv_device from data structure, nv_release returns -ENODEV.\n");
		return -ENODEV;
	}

	if(PCI_FUNC(ndev->devfn) == DS31256_PCI_FN)
	{
		if(debug & NV_MSG_RELEASE)
			printk(DFX"[device %d]release Rx&Tx DMA buffer. Received %d packets, and dropped %d.\n", MINOR(ndev->dev_id), ndev->rx_packets, ndev->rx_dropped);

		if(ndev->operation & OPERATION_RX)
			free_rx_dma(ndev->rdma);
		if(ndev->operation & OPERATION_TX)
			free_tx_dma(ndev->tdma);
		ndev->rx_dropped = 0;
		ndev->rx_packets = 0;		
	}
	free_irq(ndev->irq, ndev);
	flush_workqueue(nv_workqueue);

	if(is_buffer_init)
	{
		buf_windex = 0;
		buf_rindex = 0;
		is_buffer_init = 0;
	}
	if(debug & NV_MSG_RELEASE)
		printk(DFX"[device %d]closed device file.\n", MINOR(ndev->dev_id));
	filp->private_data = NULL;

	return 0;
}

/*
* nv_ioctl - control the nv_device,read or write the DS31256&DS26519's register
* 
* @node - device node in system
* @filp - device file pointer
* @cmd - control command
* @arg - register's address and the value will be written to
*     Note:arg high 16bits contains the register's offset and the lower contains the value
*
* Returns - 0:normal operation when write.
*           register's value when read
*/
static int nv_ioctl(struct inode *node, struct file *filp, unsigned int cmd, unsigned long arg)
{
	nvDevicePtr ndev = NULL;
	unsigned int retval = 0;
	void * ioaddr = NULL;
	if(_IOC_TYPE(cmd) != NV_IOC_MAGIC)
		return -ENOTTY;
	if(_IOC_NR(cmd) > NV_IOC_MAXNR)
		return -ENOTTY;

	ndev = (nvDevicePtr)filp->private_data;
	if(ndev == NULL)
	{
		if(debug & NV_MSG_IOCTL)
			printk(DFX"can't get nvDevice from filp->private_data, nv_ioctl returns -EINVAL.\n");
		return -EINVAL;
	}

	ioaddr = ndev->ioaddr;
	if(PCI_FUNC(ndev->devfn) == DS31256_BRIDGE_FN)
		ioaddr = MKREG_ADDR(ioaddr, DS26519_BASE_ADDRESS);

	switch(cmd)
	{
	case NV_IOC_WRITE_REG:
		write_regs(MKREG_ADDR(ioaddr, REG_ADDRESS(arg)), REG_VALUE(arg), PCI_FUNC(ndev->devfn));
		break;
	case NV_IOC_WRITE_REG_IS:
		write_regs_is(MKREG_ADDR(ioaddr, REG_ADDRESS(arg)), REG_VALUE(arg), PCI_FUNC(ndev->devfn));
		break;
	case NV_IOC_READ_REG:
		retval = MKREG_ADDVAL(REG_ADDRESS(arg), REG_VALUE(read_regs(MKREG_ADDR(ioaddr, REG_ADDRESS(arg)), PCI_FUNC(ndev->devfn))));
		break;
	case NV_IOC_RESET_PCI:
		break;
	case NV_IOC_RESET_LOCAL:
		break;
	case NV_IOC_DUMP:
		dump(ndev);
		break;
	case NV_IOC_CONFIGDMA:
		config_dma(ndev);
		break;
	case NV_IOC_EINTERRUPT:
		enable_interrupt(ndev, REG_VALUE(arg));
	default:
		return -EINVAL;
	}
	return retval;
}

static void enable_interrupt(nvDevicePtr ndev, char irq_op)
{
	if(!irq_op)
	{
		flush_scheduled_work();
		free_irq(ndev->irq, ndev);
	}
	else if(request_irq(ndev->irq, (irq_handler_t)nv_interrupt, IRQF_SHARED, "nvc", (void *)ndev))
	{
		printk(DFX"[device %d]can't get assigned irq %i\n", MINOR(ndev->dev_id), ndev->irq);
		ndev->irq = -1;
	}
}
/**************************************************************************
*                                                                        *
*                functions for PCI device driver                         *
*                                                                        *
**************************************************************************/

static int __devinit nv_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
{
	int err = 0;
	nvDevicePtr ndev;
	if(pci_enable_device(pdev))
	{
		pci_disable_device(pdev);
		return -ENODEV;
	}

	pci_request_region(pdev, 0, DRV_NAME);

	if((ndev = alloc_nv_device()) == NULL)
	{
		pci_release_region(pdev,0);
		pci_disable_device(pdev);
		return -ENOMEM;
	}
	ndev->pdev = pdev;
	init_nv_device(ndev);

	if((err = register_nv_device(ndev)))
	{
		free_nv_device(ndev);
		pci_release_region(pdev,0);
		pci_disable_device(pdev);
		if(debug & NV_MSG_PROBE)
			printk(KERN_NOTICE DFX"Error %d adding device %d\n", err, MINOR(ndev->dev_id));
		return err;
	}

	pci_set_drvdata(pdev, ndev);
	return 0;
}

static void __devexit nv_remove(struct pci_dev *pdev)
{
	nvDevicePtr ndev = pci_get_drvdata(pdev);
	free_nv_device(ndev);
	pci_set_drvdata(pdev, NULL);
	pci_release_region(pdev, 0);
	pci_disable_device(pdev);
}

static irqreturn_t nv_interrupt(int irq, void * dev, struct pt_regs * regs)
{
	nvDevicePtr ndev = (nvDevicePtr)dev;
	
	if(ndev == NULL || ndev->irq != irq )
		return IRQ_NONE;

	queue_work(nv_workqueue, &ndev->my_work);

	return IRQ_HANDLED;
}
static void nv_do_work(struct work_struct * work)
{	
	nvDevicePtr ndev = container_of(work, nvDevice, my_work);
	if(ndev == NULL)
		return;
		
	fill_time_field();

	switch(PCI_FUNC(ndev->devfn))
	{
	case DS31256_PCI_FN:
		proc_data_packet(ndev);
		break;
	case DS31256_BRIDGE_FN:
		proc_data_status(ndev);
		break;
	}
}
static void proc_data_packet(nvDevicePtr ndev)
{
	unsigned short reg_value;
	int cnt_to_write, cnt_read, windex, rindex;

	if(ndev == NULL)
		return;
	reg_value = REG_VALUE(read_regs(MKREG_ADDR(ndev->ioaddr, DS31256_GCR_SDMA), DS31256_PCI_FN));
	/*if((reg_value & 0x0C20) == 0x00)
		return;*/
	if((reg_value & IMASK_ISDMA_ROVEL) )
	{
		ndev->rx_dropped++;
		if(debug & NV_MSG_INTERRUPT)
			printk("device %d ROVEL error, dropped = %d\n", MINOR(ndev->dev_id), ndev->rx_dropped);
	}
	if((reg_value & IMASK_ISDMA_RDQWE))
	{
		ndev->rx_dropped++;
		if(debug & NV_MSG_INTERRUPT)
			printk("device %d RDQWE error, dropped = %d\n", MINOR(ndev->dev_id), ndev->rx_dropped);
	}

	windex = buf_windex;
	rindex = buf_rindex;
	cnt_to_write = get_buf_cnt_to_write(windex, rindex);	

	if((cnt_read = read_from_dma(ndev, packet_buffer[windex], BUF_PACKET_SIZE)) > 0)
	{
		if(cnt_to_write > 0)
		{			
			set_rw_pos(&buf_windex, 1);
			ndev->rx_packets++;
			if((debug & NV_MSG_INTERRUPT) && ((ndev->rx_packets % PRINT_RX_PACKETS) == 0))
				printk(DFX"board %d has received %d packets.\n", PCI_SLOT(ndev->devfn) / 2, ndev->rx_packets);
		}
		else
		{			
			ndev->rx_dropped++;
			if((debug & NV_MSG_INTERRUPT) && ((ndev->rx_dropped % PRINT_RX_DROPPED) == 0))
				printk(DFX"board %d has dropped %d packets.\n", PCI_SLOT(ndev->devfn) / 2, ndev->rx_dropped);
		}
	}
}
static void proc_data_status(nvDevicePtr ndev)
{
	int offset;
	void * ioaddr;
	unsigned short gfis_value, framer;
	unsigned char reg_value;

	if(ndev == NULL)
		return;

	ioaddr = MKREG_ADDR(ndev->ioaddr, DS26519_BASE_ADDRESS);
	gfis_value = REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS26519_GR_GFISR2), DS31256_BRIDGE_FN));
	gfis_value = (gfis_value << 8) | REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS26519_GR_GFISR1), DS31256_BRIDGE_FN));
	framer = 0;
	do
	{
		if((gfis_value >> framer) & 0x01)
		{
			offset = 0x200 * (framer % 8) + 0x2000 * (framer / 8);
			reg_value = REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS26519_FRAMER_RIIR + offset), DS31256_BRIDGE_FN)) & 0xFF;
			if( reg_value & IMASK_RIIR_RLS1)
			{
				reg_value = REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS26519_FRAMER_RLS1 + offset), DS31256_BRIDGE_FN)) & 0xFF;
				encap_status_packet(PCI_SLOT(ndev->devfn) / 2, framer, reg_value);
				write_regs(MKREG_ADDR(ioaddr, DS26519_FRAMER_RLS1 + offset), reg_value, DS31256_BRIDGE_FN);//clear interrupt bit locations
			}
		}
		framer++;
	}
	while(framer < MAX_FRAMER_NUM);
}
static void encap_status_packet(unsigned char device, unsigned char port, unsigned char status)
{
	int cnt_to_write, index;
	unsigned char bytecnt = 1;
	if(device >= MAX_DEVICE_NUM || port >= MAX_FRAMER_NUM)
		return;
	index = 0;
	while(index < 8)
	{
		if((status >> index) & 0x01)
		{
			if((cnt_to_write = get_buf_cnt_to_write(buf_windex, buf_rindex)) <= 0)
			{
				printk(DFX"oh, data buffer is full, God bless you!");
				break;
			}
			packet_buffer[buf_windex][OFF_LENGTH_HIGH] = 0;
			packet_buffer[buf_windex][OFF_LENGTH_LOW] = PACKET_HEAD_LEN - 2 + bytecnt;
			packet_buffer[buf_windex][OFF_EVENT_TYPE] = PACKET_TYPE_STATUS;
			packet_buffer[buf_windex][OFF_BOARD] = device;
			packet_buffer[buf_windex][OFF_PORT] = port;
			packet_buffer[buf_windex][OFF_STATUS] = packet_status_type[index];
			set_rw_pos(&buf_windex, 1);
		}
		index++;
	}
}
static void set_rw_pos(unsigned int * rw_pos, int inc)
{
	*rw_pos = (*rw_pos + inc) % BUF_PACKET_NUM;
}
static int get_buf_cnt_to_write(int windex, int rindex)
{
	if(windex >= rindex)
		return rindex + BUF_PACKET_NUM - windex - 1;
	else
		return rindex - windex - 1;
}
static int get_buf_cnt_to_read(int rindex, int windex)
{
	if(rindex > windex)
		return windex + BUF_PACKET_NUM - rindex;
	else
		return windex - rindex;
}
static void fill_time_field(void)
{
	struct timeval timestamp;
	char * buff = packet_buffer[buf_windex];
	do_gettimeofday(&timestamp);
	memset(buff, 0, BUF_PACKET_SIZE);
	buff[OFF_SECOND_D4] = (timestamp.tv_sec >> 24) & 0xFF;
	buff[OFF_SECOND_D3] = (timestamp.tv_sec >> 16) & 0xFF;
	buff[OFF_SECOND_D2] = (timestamp.tv_sec >> 8) & 0xFF;
	buff[OFF_SECOND_D1] = (timestamp.tv_sec) & 0xFF;
	buff[OFF_USECOND_D4] = (timestamp.tv_usec >> 24) & 0xFF;
	buff[OFF_USECOND_D3] = (timestamp.tv_usec >> 16) & 0xFF;
	buff[OFF_USECOND_D2] = (timestamp.tv_usec >> 8) & 0xFF;
	buff[OFF_USECOND_D1] = (timestamp.tv_usec) & 0xFF;
}
#ifdef CONFIG_PM
static int nv_suspend(struct pci_dev *pdev, pm_message_t state)
{
	return 0;
}

static int nv_resume(struct pci_dev *pdev)
{
	return 0;
}
#endif

static int __init nv_init_module(void)
{
	int i;
	if(debug & NV_MSG_DRIVER)
	{
		printk(KERN_INFO DFX "%s, %s\n", DRV_DESCRIPTION, DRV_VERSION);
		printk(KERN_INFO DFX "%s\n", DRV_COPYRIGHT);
	}

	for(i = 0; i < BUF_PACKET_NUM; i++)
	{
		if((packet_buffer[i] = (char *)kmalloc(BUF_PACKET_SIZE, GFP_KERNEL)) == NULL)
		{
			release_driver_buffer();
			printk(DFX"need more memory to allocated to driver.\n");
			return -ENOBUF;
		}		
	}
	nv_workqueue = create_singlethread_workqueue("nvworkqueue");
	if(!nv_workqueue)
	{
		printk(DFX"can't create work queue.\n");
		release_driver_buffer();
		return -ENOBUF;
	}
	return pci_register_driver(&nv_pci_driver);
}

static void __exit nv_exit_module(void)
{
	pci_unregister_driver(&nv_pci_driver);
	release_driver_buffer();
	destroy_workqueue(nv_workqueue);
}
static void release_driver_buffer(void)
{
	int i;
	for(i = 0; i < BUF_PACKET_NUM; i++)
	{
		if(packet_buffer[i] != NULL)
			kfree(packet_buffer[i]);
	}
}
module_init(nv_init_module);
module_exit(nv_exit_module);

/**************************************************************************
*                                                                        *
*                functions for user operations                           *
*                                                                        *
**************************************************************************/
static dmaRxDevPtr alloc_rx_dma(void)
{
	dmaRxDevPtr rdma = (dmaRxDevPtr)kmalloc(sizeof(dmaRxDev), GFP_KERNEL);
	if(rdma == NULL)
		goto err;

	rdma->freeQStart = NULL;
	rdma->doneQStart = NULL;
	rdma->descQStart = NULL;
	rdma->dataBufAddr = NULL;

	//r_dma->freeQStart = (nvRfqPtr)kmalloc(RX_DESC_QUEUE_SIZE * sizeof(nvRfq), GFP_KERNEL);
	rdma->freeQStart = (dmaRxFreeDescPtr)kmalloc(RX_DESC_QUEUE_SIZE * sizeof(dmaRxFreeDesc), GFP_KERNEL);
	if(rdma->freeQStart == NULL)
		goto err;
        memset(rdma->freeQStart, 0, RX_DESC_QUEUE_SIZE * sizeof(dmaRxFreeDesc));
	//r_dma->doneQStart = (nvRdqPtr)kmalloc(RX_DESC_QUEUE_SIZE * sizeof(nvRdq), GFP_KERNEL);
	rdma->doneQStart = (dmaRxDoneDescPtr)kmalloc(RX_DESC_QUEUE_SIZE * sizeof(dmaRxDoneDesc), GFP_KERNEL);
	if(rdma->doneQStart == NULL)
		goto err;
        memset(rdma->doneQStart, 0, RX_DESC_QUEUE_SIZE * sizeof(dmaRxDoneDesc));
	rdma->descQStart = (dmaDescPtr)kmalloc(RX_DESC_QUEUE_SIZE * sizeof(dmaDesc), GFP_KERNEL);
	if(rdma->descQStart == NULL)
		goto err;
        memset(rdma->descQStart, 0, RX_DESC_QUEUE_SIZE * sizeof(dmaDesc));
	//r_dma->descQStart = (nvPacketPtr)kmalloc(RX_DESC_QUEUE_SIZE * sizeof(nvPacket), GFP_KERNEL);
	rdma->dataBufAddr = (unsigned char *)kmalloc(RX_DESC_QUEUE_SIZE * DATA_BUFFER_SIZE, GFP_KERNEL);
	if(rdma->dataBufAddr != NULL)
        {
            memset(rdma->dataBufAddr, 0, RX_DESC_QUEUE_SIZE * DATA_BUFFER_SIZE);
            return rdma;
        }
        err:free_rx_dma(rdma);
	return NULL;
}

static dmaTxDevPtr alloc_tx_dma(void)
{
	dmaTxDevPtr tdma = (dmaTxDevPtr)kmalloc(sizeof(dmaTxDev), GFP_KERNEL);
	if(tdma == NULL)
		goto err;

	tdma->doneQStart = NULL;
	tdma->descQStart = NULL;
	tdma->pendQStart = NULL;

	//t_dma->doneQStart = (nvTdqPtr)kmalloc(TX_DESC_QUEUE_SIZE * sizeof(nvTdq), GFP_KERNEL);
	tdma->doneQStart = (dmaTxDoneDescPtr)kmalloc(TX_DESC_QUEUE_SIZE * sizeof(dmaTxDoneDesc), GFP_KERNEL);
	if(tdma->doneQStart == NULL)
		goto err;
        memset(tdma->doneQStart, 0, TX_DESC_QUEUE_SIZE * sizeof(dmaTxDoneDesc));
	//t_dma->pendQStart = (nvTpqPtr)kmalloc(TX_DESC_QUEUE_SIZE * sizeof(nvTpq), GFP_KERNEL);
	tdma->pendQStart = (dmaTxPendDescPtr)kmalloc(TX_DESC_QUEUE_SIZE * sizeof(dmaTxPendDesc), GFP_KERNEL);
	if(tdma->pendQStart == NULL)
		goto err;
        memset(tdma->pendQStart, 0, TX_DESC_QUEUE_SIZE * sizeof(dmaTxPendDesc));
	//t_dma->descQStart = (nvPacketPtr)kmalloc(TX_DESC_QUEUE_SIZE * sizeof(nvPacket), GFP_KERNEL);
	tdma->descQStart = (dmaDescPtr)kmalloc(TX_DESC_QUEUE_SIZE * sizeof(dmaDesc), GFP_KERNEL);
	if(tdma->descQStart != NULL)
	{
		memset(tdma->descQStart, 0, TX_DESC_QUEUE_SIZE * sizeof(dmaDesc));
		return tdma;
	}

        err:free_tx_dma(tdma);
	return NULL;
}

static void free_rx_dma(dmaRxDevPtr rdma)
{
	if(rdma != NULL)
	{
		if(rdma->descQStart != NULL)
			kfree(rdma->descQStart);

		if(rdma->freeQStart != NULL)
			kfree(rdma->freeQStart);

		if(rdma->doneQStart != NULL)
			kfree(rdma->doneQStart);

		if(rdma->dataBufAddr != NULL)
			kfree(rdma->dataBufAddr);

		kfree(rdma);
	}
}

static void free_tx_dma(dmaTxDevPtr tdma)
{
	if(tdma != NULL)
	{
		if(tdma->doneQStart != NULL)
			kfree(tdma->doneQStart);

		if(tdma->pendQStart != NULL)
			kfree(tdma->pendQStart);

		if(tdma->descQStart != NULL)
		{
			free_tx_data(tdma->descQStart);
			kfree(tdma->descQStart);
		}

		kfree(tdma);
	}
}

static void free_tx_data(const dmaDescPtr descStart)
{
	int index = 0;
	if(descStart != NULL)
		while(index < TX_DESC_QUEUE_SIZE)
		{
			free_user_data(descStart + index);
			index++;
		}
}

static void free_user_data(const dmaDescPtr ptr)
{
    if(ptr != NULL && ptr->dataAddr != 0)
    {
        kfree(phys_to_virt(ptr->dataAddr));
        ptr->dataAddr = 0;
    }
}
static int read_from_dma(nvDevicePtr ndev, char * buff, size_t count)
{
	unsigned short rd_ind,wr_ind,bytecnt;
	unsigned short desc_ind;//packet descriptor index
	dmaRxDoneDescPtr dDescPtr;
	int cnt,err;

	if(buff == NULL)
		return -ENOBUF;

	rd_ind = REG_VALUE(read_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDQRP), DS31256_PCI_FN));// receive done queue descriptor host read index
	wr_ind = REG_VALUE(read_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDQWP), DS31256_PCI_FN));//receive done queue descriptor DMA wriete index
	//read_regs(MKREG_ADDR(ndev->ioaddr, DS31256_GCR_SDMA), DS31256_PCI_FN);

	//check Rx done queue to see any packets received yet
	if(wr_ind >= rd_ind)
		cnt = wr_ind - rd_ind;
	else
		cnt = RX_DESC_QUEUE_SIZE + wr_ind - rd_ind;

	if(cnt <= 0)
		return 0;

	dDescPtr = ndev->rdma->doneQStart + rd_ind;
	desc_ind = dDescPtr->desc & 0xFFFF;

	if(((dDescPtr->desc >> 24) & 0x07) != 0x00)//buffer count = 000 indicates that a complete packet has been received
		return 0;

	if(((dDescPtr->desc >> 30) & 0x01) == 0x00)//a packet has been fully received or an error has been detected
		return 0;

	bytecnt = get_packet_size(desc_ind, ndev->rdma->descQStart);
	if(bytecnt > count - PACKET_HEAD_LEN)//two bytes for length, eigth bytes for timestamp, one byte reserved,one bytes for board id, one byte for channel,one byte for status 
		return -ENOBUF;
	if(bytecnt <= 0)
		return 0;

	buff[OFF_RESERVED] = 0x00; //reserved
	buff[OFF_EVENT_TYPE] = PACKET_TYPE_DATA;//packet type 0x00 = discarded packet, 0x01 = valid packet
	buff[OFF_BOARD] = PCI_SLOT(ndev->devfn) / 2;//board id
	buff[OFF_CHANNEL] = (dDescPtr->desc >> 16) & 0xFF;//channel
	buff[OFF_STATUS] = (dDescPtr->desc >> 27) & 0x07;//packet status	
	if(buff[OFF_STATUS] != 0x00)//discarded packet
	{
		buff[OFF_EVENT_TYPE] = PACKET_TYPE_DISCARD;
		bytecnt = 1;
	}
	buff[OFF_LENGTH_HIGH] = ((bytecnt + PACKET_HEAD_LEN - 2) >> 8) & 0xFF;
	buff[OFF_LENGTH_LOW] = (bytecnt + PACKET_HEAD_LEN - 2) & 0xFF;
	
	if((buff[OFF_STATUS] == 0x00) && (err = get_packet(buff + PACKET_HEAD_LEN, desc_ind, ndev->rdma->descQStart, bytecnt)))
	{
		printk(DFX"get packet ERROR( %x )\n", -err);
		return err;
	}
	//printk("board %d get packet from %d = %d bytes\n", PCI_SLOT(ndev->devfn) / 2, rd_ind, bytecnt + PACKET_HEAD_LEN);
	write_rx_done_queue(ndev->ioaddr, rd_ind + 1);//increase Rx done queue read pointer;
	write_rx_free_queue(ndev->ioaddr, desc_ind, ndev->rdma);	

	return bytecnt + PACKET_HEAD_LEN;
}
static void write_rx_done_queue(void * ioaddr, unsigned short rd_ind)
{
	if(rd_ind >= RX_DESC_QUEUE_SIZE)
		write_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RDQRP), 0, DS31256_PCI_FN);
	else
		write_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RDQRP), rd_ind, DS31256_PCI_FN);
}
static void write_rx_free_queue(void * ioaddr, unsigned short desc_ind, dmaRxDevPtr rdma_ptr)
{
	dmaDescPtr desc_ptr;
	dmaRxFreeDescPtr rfd_ptr;
	int cnt;
	unsigned char bufs;
	unsigned short wr_ind, rd_ind, next_ind;
	if(desc_ind >= RX_DESC_QUEUE_SIZE)
		return;
	desc_ptr = rdma_ptr->descQStart + desc_ind;
	bufs = (desc_ptr->desc1 >> 29) & 0x07;
	next_ind = desc_ind;
	while( bufs == 0x01 || bufs == 0x02)
	{
		rd_ind = REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBRP), DS31256_PCI_FN));
		wr_ind = REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBWP), DS31256_PCI_FN));
		if(rd_ind > wr_ind)
			cnt = rd_ind - wr_ind - 1;
		else
			cnt = RX_DESC_QUEUE_SIZE + rd_ind - wr_ind - 1;
		if(cnt > 0)
		{
			rfd_ptr = rdma_ptr->freeQStart + wr_ind;
			rfd_ptr->dataAddr = desc_ptr->dataAddr;
			rfd_ptr->desc = next_ind & 0xFFFF;
			if(wr_ind == RX_DESC_QUEUE_SIZE - 1)
				write_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBWP), 0, DS31256_PCI_FN);
			else
				write_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBWP), wr_ind + 1, DS31256_PCI_FN);
		}

		next_ind = desc_ptr->desc1 & 0xFFFF;
		desc_ptr = rdma_ptr->descQStart + next_ind;
		bufs = (desc_ptr->desc1 >> 29) & 0x07;
	}

	if(bufs == 0x04)
	{
		rd_ind = REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBRP), DS31256_PCI_FN));
		wr_ind = REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBWP), DS31256_PCI_FN));
		if(rd_ind > wr_ind)
			cnt = rd_ind - wr_ind - 1;
		else
			cnt = RX_DESC_QUEUE_SIZE + rd_ind - wr_ind - 1;
		if(cnt > 0)
		{
			rfd_ptr = rdma_ptr->freeQStart + wr_ind;
			rfd_ptr->dataAddr = desc_ptr->dataAddr;
			rfd_ptr->desc = next_ind & 0xFFFF;
			if(wr_ind == RX_DESC_QUEUE_SIZE - 1)
				write_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBWP), 0, DS31256_PCI_FN);
			else
				write_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBWP), wr_ind + 1, DS31256_PCI_FN);
		}
	}
}
static int get_packet(void * to, const unsigned short desc_ind, const dmaDescPtr start, const unsigned short count)
{
	int cnt = 0;
	dmaDescPtr dDescPtr;		
	unsigned short bufStatus;
	unsigned short byteCount;

	if(to == NULL)
		return -ENOBUF;
	if(desc_ind >= RX_DESC_QUEUE_SIZE)
		return -ERFQOVER;

	dDescPtr = start + desc_ind;
	bufStatus = (dDescPtr->desc1 >> 29) & 0x07;
	byteCount = (dDescPtr->desc1 >> 16) & 0x1FFF;
	
	while(bufStatus == 0x01 || bufStatus == 0x02)
	{
		if( (cnt + byteCount) > count)//buff overflow
			return -ENOBUF;
		memcpy(to + cnt, phys_to_virt(dDescPtr->dataAddr), byteCount);
		cnt += byteCount;
		dDescPtr = start + (dDescPtr->desc1 & 0xFFFF);
		byteCount = (dDescPtr->desc1 >> 16) & 0x1FFF;
		bufStatus = (dDescPtr->desc1 >> 29) & 0x07;
	}

	if( (cnt + byteCount) > count)//buff overflow
		return -ENOBUF;
	if(bufStatus == 0x04)
		memcpy(to + cnt, phys_to_virt(dDescPtr->dataAddr), byteCount);
	return 0;
}

static int get_packet_size(const unsigned short desc_ind, const dmaDescPtr start)
{
	int cnt = 0;
	dmaDescPtr dPtr = start + desc_ind;
	unsigned char bufs = (dPtr->desc1 >> 29) & 0x07;
	while( bufs == 0x01 || bufs == 0x02)
	{
		cnt += (dPtr->desc1 >> 16) & 0x1FFF;
		dPtr = start + (dPtr->desc1 & 0xFFFF);
		bufs = (dPtr->desc1 >> 29) & 0x07;
	}
	if(bufs == 0x04)
		cnt += (dPtr->desc1 >> 16) & 0x1FFF;

	return cnt;
}

static int write_to_dma(nvDevicePtr ndev, const char __user *buff, size_t count)
{
	int err = 0;
	char data[count];
	void * packet = NULL;

	err = copy_from_user(data, (void *)buff, (unsigned long)count);
	if(err)
	{
		if(debug & NV_MSG_WDMA)
			printk(DFX"[device %d]can't copy data from user buff, error code %d\n", MINOR(ndev->dev_id), err);
		return err;
	}
	packet = kmalloc(count - 4, GFP_KERNEL);
	if( packet == NULL)
	{
		if(debug & NV_MSG_WDMA)
			printk(DFX"[device %d]I'm so sorry, there is no enough memory to alloc. user data ignored. I return -ENOMEM\n", MINOR(ndev->dev_id));
		return -ENOMEM;
	}
	memcpy(packet, data + 4, count - 4);

        err = queue_tx_pending_packet(ndev, data[0], VIRT_TO_PHYS(packet), (unsigned long)count - 4, 0);//data[0] = HDLC channel
	if(err)
		kfree(packet);
        
	//Check tx done queue, if error occured, requeue packets
	check_tx_done_queue(ndev);

	return err;
}
static void check_tx_done_queue(nvDevicePtr ndev)
{
	int cnt = 0;
	unsigned short rd_ind, wr_ind, desc_ind;
        dmaTxDoneDescPtr dDescPtr;

	wr_ind = REG_VALUE(read_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDQWP), DS31256_PCI_FN));
	rd_ind = REG_VALUE(read_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDQRP), DS31256_PCI_FN));

	//check to see how many entries are in the Tx done queue
	if(wr_ind >= rd_ind)
		cnt = wr_ind - rd_ind;
	else
		cnt = TX_DESC_QUEUE_SIZE + wr_ind - rd_ind;

	while(cnt-- > 0)
	{
            dDescPtr = ndev->tdma->doneQStart + rd_ind;
            desc_ind = dDescPtr->desc & 0xFFFF;
		if(((dDescPtr->desc >> 26) & 0x07) != 0x00)
                    requeue_tx_packet(desc_ind, ndev);
                else
                    free_user_data(ndev->tdma->descQStart + desc_ind);
		if(rd_ind++ == TX_DESC_QUEUE_SIZE - 1)
			rd_ind = 0;
	}
	write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDQRP), rd_ind, DS31256_PCI_FN);
}

static int requeue_tx_packet(const unsigned short desc_ind, nvDevicePtr ndev)
{
	unsigned int data;
	char channel;
	unsigned long count;
        dmaDescPtr descPtr = ndev->tdma->descQStart + desc_ind;
        count = (descPtr->desc1 >> 16) & 0x1FFF;
	channel = descPtr->desc2 & 0xFF;	
	data =descPtr->dataAddr;

	return queue_tx_pending_packet(ndev, channel, data, count, 1);
}
static int queue_tx_pending_packet(nvDevicePtr ndev, const  unsigned short channel, const unsigned int data, const unsigned long count, const char pri)
{
	unsigned short wr_ind, rd_ind;
        dmaTxPendDescPtr pDescPtr;
        dmaDescPtr descPtr;
	int cnt;
	
	wr_ind = REG_VALUE(read_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TPQWP), DS31256_PCI_FN));
	rd_ind = REG_VALUE(read_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TPQRP), DS31256_PCI_FN));
	//check free space in Tx pending queue
	
	if(rd_ind > wr_ind)
		cnt = rd_ind - wr_ind - 1;
	else
		cnt = TX_DESC_QUEUE_SIZE + rd_ind - wr_ind - 1;
	if(cnt <= 0)
	{
		printk(DFX"[device %d]Tx pending queue full\n", MINOR(ndev->dev_id));
		return -ETPQFULL;
	}

	//read SDMA register to clear any previously set status bits
	/*
	status = REG_VALUE(read_regs(MKREG_ADDR(ndev->ioaddr, DS31256_GCR_SDMA), DS31256_PCI_FN));
	if(debug & NV_MSG_WDMA)
	printk(DFX"[device %d]SDMA status register value:%d\n", MINOR(ndev->dev_id), status);
	*/
        pDescPtr = ndev->tdma->pendQStart + wr_ind;
	pDescPtr->desc = (channel << 16) | (pri << 24);//assign pending queue descriptor
        descPtr = ndev->tdma->descQStart + (pDescPtr->desc & 0xFFFF);

	descPtr->dataAddr = data;
        descPtr->desc1 = 0x80000000 | (count << 16);
        descPtr->desc2 = 0xFF & channel;
        descPtr->desc3 = 0;

	//Advance the Tx pending write pointer
	if(wr_ind++ == TX_DESC_QUEUE_SIZE - 1)
		wr_ind = 0;
	write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TPQWP), wr_ind, DS31256_PCI_FN);
	
	return 0;
}
static int config_dma(nvDevicePtr ndev)
{
	unsigned short desc_ind;
        dmaRxFreeDescPtr fDescPtr;
        dmaTxPendDescPtr pDescPtr;
	if(ndev == NULL || ndev->ioaddr == NULL)
		return -EINVAL;
	if((ndev->operation & OPERATION_RX) && ndev->rdma == NULL)
		return -EINVAL;
	if((ndev->operation & OPERATION_TX) && ndev->tdma == NULL)
		return -EINVAL;

	if(ndev->operation & OPERATION_RX)
	{
		for(desc_ind = 0; desc_ind < RX_DESC_QUEUE_SIZE; desc_ind++)
		{
                    fDescPtr = ndev->rdma->freeQStart + desc_ind;
                    fDescPtr->dataAddr = VIRT_TO_PHYS((ndev->rdma->dataBufAddr + desc_ind * DATA_BUFFER_SIZE));
                    fDescPtr->desc = desc_ind & 0xFFFF;
		}

		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDBA0),VIRT_TO_PHYS(ndev->rdma->descQStart) & 0xffff, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDBA1), VIRT_TO_PHYS(ndev->rdma->descQStart) >> 16, DS31256_PCI_FN);

		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RFQBA0),VIRT_TO_PHYS(ndev->rdma->freeQStart) & 0xffff, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RFQBA1), VIRT_TO_PHYS(ndev->rdma->freeQStart) >> 16, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RFQLBWP), (desc_ind - 1) & 0xFFFF, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RFQLBRP), 0, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RFQEA), (RX_DESC_QUEUE_SIZE - 1) & 0xffff, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RFQSBSA), (RX_DESC_QUEUE_SIZE - 1)& 0xffff, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RFQSBWP), 0, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RFQSBRP), 0, DS31256_PCI_FN);

		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDQBA0), VIRT_TO_PHYS(ndev->rdma->doneQStart) & 0xffff, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDQBA1), VIRT_TO_PHYS(ndev->rdma->doneQStart) >> 16, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDQWP), 0, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDQRP), 0, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDQEA), (RX_DESC_QUEUE_SIZE - 1) & 0xffff, DS31256_PCI_FN);

		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RLBS), DATA_BUFFER_SIZE & 0xffff, DS31256_PCI_FN);
                write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RSBS), 0, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_RDMA_RDQFFT), 0xff, DS31256_PCI_FN);
	}
	if(ndev->operation & OPERATION_TX)
	{
		for(desc_ind = 0; desc_ind < TX_DESC_QUEUE_SIZE; desc_ind++)
		{
                    pDescPtr = ndev->tdma->pendQStart + desc_ind;
                    pDescPtr->desc = desc_ind & 0xFFFF;
		}
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDBA0), VIRT_TO_PHYS(ndev->tdma->descQStart) & 0xffff, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDBA1), VIRT_TO_PHYS(ndev->tdma->descQStart) >> 16, DS31256_PCI_FN);

		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TPQBA0), VIRT_TO_PHYS(ndev->tdma->pendQStart) & 0xffff, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TPQBA1), VIRT_TO_PHYS(ndev->tdma->pendQStart) >> 16, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TPQWP), 0, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TPQRP), 0, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TPQEA), (TX_DESC_QUEUE_SIZE - 1) & 0xffff, DS31256_PCI_FN);

		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDQBA0), VIRT_TO_PHYS(ndev->tdma->doneQStart) & 0xffff, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDQBA1), VIRT_TO_PHYS(ndev->tdma->doneQStart) >> 16, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDQWP), 0, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDQRP), 0, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDQEA), (TX_DESC_QUEUE_SIZE - 1) & 0xffff, DS31256_PCI_FN);
		write_regs(MKREG_ADDR(ndev->ioaddr, DS31256_TDMA_TDQFFT), 0xff, DS31256_PCI_FN);
	}
	return 0;
}

static unsigned int read_regs(void * addr, char flag)
{
	if(flag == DS31256_PCI_FN)
		return ioread16(addr);
	else if(flag == DS31256_BRIDGE_FN)
	{
		ioread8(addr);//ignore local read delay
		return ioread8(addr);
	}
	else
		return -EINVAL;
}

static void write_regs(void * addr, unsigned short value, char flag)
{
	if(flag == DS31256_PCI_FN)
		iowrite16(value, addr);
	else if(flag == DS31256_BRIDGE_FN)
		iowrite8(value & 0xFF, addr);
}
static void write_regs_is(void * addr, unsigned short value, char flag)
{
    int count = 50;
	if(flag == DS31256_PCI_FN)
	{
		write_regs(addr, value, flag);
		msleep(1);
		while((read_regs(addr, flag) & 0x8000) && count--)
		{
			msleep(2);//waiting for operation completes.
		}
	}
}
static nvDevicePtr alloc_nv_device(void)
{
	nvDevicePtr ndev;
	if((ndev = (nvDevicePtr)kmalloc(sizeof(nvDevice), GFP_KERNEL)) == NULL)
		return NULL;
	ndev->rdma = NULL;
	ndev->tdma = NULL;
	ndev->pdev = NULL;
	ndev->ioaddr = NULL;
	return ndev;
}

static int register_nv_device(nvDevicePtr ndev)
{
	int err = 0;
	if(MAJOR(ndev->dev_id))
	{
		err = register_chrdev_region(ndev->dev_id, 1, "nvc");
	}
	else
	{
		err = alloc_chrdev_region(&ndev->dev_id, MINOR(ndev->dev_id), 1, "nvc");
		nmajor = MAJOR(ndev->dev_id);
	}
	if(err)
		return err;

	cdev_init(&ndev->device, &nv_fops);
	ndev->device.owner = THIS_MODULE;
	return cdev_add(&ndev->device, ndev->dev_id, 1);
}

static int init_nv_device(nvDevicePtr ndev)
{
	if(ndev != NULL && ndev->pdev != NULL)
	{		
		ndev->devfn = ndev->pdev->devfn;
		ndev->dev_id = MKDEV(nmajor, PCI_FUNC(ndev->devfn) + PCI_SLOT(ndev->devfn));
		ndev->irq = ndev->pdev->irq;
		ndev->ioaddr = ioremap(pci_resource_start(ndev->pdev,0),pci_resource_len(ndev->pdev,0));
		ndev->operation = OPERATION_NONE;
		ndev->rx_dropped = 0;
		ndev->rx_packets = 0;

		if(ndev->ioaddr == NULL)
		{
			printk(KERN_ERR DFX"Cannot remap MMIO, aborting\n");
			return -EIO;
		}
		if(PCI_FUNC(ndev->devfn) == DS31256_PCI_FN)
		{
			pci_set_master(ndev->pdev);
			ndev->operation = OPERATION_MODE;//|OPERATION_TX;/*default:enable Rx&Tx channel*/
			return pci_set_dma_mask(ndev->pdev, DMA_32BIT_MASK);
		}
		pci_write_config_word(ndev->pdev, PCI_COMMAND, 0x146);
	}
	return 0;
}

static void free_nv_device(nvDevicePtr ndev)
{
	if(ndev != NULL)
	{
		if(ndev->ioaddr != NULL)
			iounmap(ndev->ioaddr);
		ndev->pdev = NULL;
		unregister_chrdev_region(ndev->dev_id, 1);
		cdev_del(&ndev->device);
		kfree(ndev);
	}
}
static void print_rdma(const dmaRxDevPtr rdma)
{
    if(rdma == NULL)
        return;
    printk(DFX"Rx DMA addr:%08X\n",(unsigned int)rdma);
    printk(DFX"Rx DMA RDQ base addr:%08X\n",(unsigned int)rdma->doneQStart);
    printk(DFX"Rx DMA RFQ base addr:%08X\n",(unsigned int)rdma->freeQStart);
    printk(DFX"Rx DMA DQ base addr:%08X\n",(unsigned int)rdma->descQStart);
    printk(DFX"Rx DMA buffer base addr:%08X\n",(unsigned int)rdma->dataBufAddr);
}
static void print_tdma(const dmaTxDevPtr tdma)
{
    if(tdma == NULL)
        return;
    printk(DFX"Tx DMA addr:%08X\n",(unsigned int)tdma);
    printk(DFX"Tx DMA TDQ base addr:%08X\n",(unsigned int)tdma->doneQStart);
    printk(DFX"Tx DMA TPQ base addr:%08X\n",(unsigned int)tdma->pendQStart);
    printk(DFX"Tx DMA DQ base addr:%08X\n",(unsigned int)tdma->descQStart);
}
static void dump_tx(void * ioaddr, dmaTxDevPtr txDev)
{
    dmaTxPendDescPtr pDescPtr;
    dmaDescPtr descPtr;
    dmaTxDoneDescPtr dDescPtr;    
    unsigned short desc;
    descPtr = txDev->descQStart;
    printk("Transtmit packet descriptors\n");
    for(desc = 0; desc < TX_DESC_QUEUE_SIZE; desc++)
    {
        printk(" Tx packet descriptor[%d] dataAddr : %08X, desc1 : %08X, desc2 : %08X, desc3 : %08X\n", desc, descPtr->dataAddr,descPtr->desc1, descPtr->desc2,descPtr->desc3);
        descPtr++;
    }
    pDescPtr = txDev->pendQStart;
    printk("Transtmit pending queues\n");
    printk("Tx Read Pointer : %04X ; Write Pointer : %04X\n", REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_TDMA_TPQRP), DS31256_PCI_FN)), REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_TDMA_TPQWP), DS31256_PCI_FN)));
    for(desc = 0; desc < TX_DESC_QUEUE_SIZE; desc++)
    {
        printk("Tx pending queue descriptor[%d] desc : %08X\n", desc, pDescPtr->desc);
        pDescPtr++;
    }

    dDescPtr = txDev->doneQStart;
    printk("Transmit done queues\n");
    printk("Tx Read Pointer : %04X ; Write Pointer : %04X\n", REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_TDMA_TDQRP), DS31256_PCI_FN)), REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_TDMA_TDQWP), DS31256_PCI_FN)));
    for(desc = 0; desc < TX_DESC_QUEUE_SIZE; desc++)
    {
        printk("Tx done queue descriptor[%d] desc : %08X\n", desc, dDescPtr->desc);
        dDescPtr++;
    }
}

static void dump_rx(void * ioaddr, dmaRxDevPtr rxDev)
{
    dmaRxFreeDescPtr fDescPtr;
    dmaRxDoneDescPtr dDescPtr;
    dmaDescPtr descPtr;
    unsigned short desc;
    printk("Receive packet descriptors\n");
    descPtr = rxDev->descQStart;
    for(desc = 0; desc < RX_DESC_QUEUE_SIZE; desc++)
    {
        printk(" Rx packet descriptor[%d] dataAddr : %08X, desc1 : %08X, desc2 : %08X, desc3 : %08X\n", desc, descPtr->dataAddr,descPtr->desc1, descPtr->desc2,descPtr->desc3);
        descPtr++;
    }
    printk("Receive free queues\n");
    printk("Rx Read Pointer : %04X ; Write Pointer : %04X\n", REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBRP), DS31256_PCI_FN)), REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RFQLBWP), DS31256_PCI_FN)));
    fDescPtr = rxDev->freeQStart;
    for(desc = 0; desc < RX_DESC_QUEUE_SIZE; desc++)
    {
        printk("Rx free queue descriptor[%d] dataAddr : %08X, desc : %08X\n", desc, fDescPtr->dataAddr, fDescPtr->desc);
        fDescPtr++;
    }
    printk("Receive done queues\n");
    printk("Rx Read Pointer : %04X ; Write Pointer : %04X\n", REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RDQRP), DS31256_PCI_FN)), REG_VALUE(read_regs(MKREG_ADDR(ioaddr, DS31256_RDMA_RDQWP), DS31256_PCI_FN)));
    dDescPtr = rxDev->doneQStart;
    for(desc = 0; desc < RX_DESC_QUEUE_SIZE; desc++)
    {
        printk("Rx done queue descriptor[%d] desc : %08X\n", desc, dDescPtr->desc);
        dDescPtr++;
    }
}
static void dump(nvDevicePtr ndev)
{
	if(OPERATION_MODE & OPERATION_RX)
	{
		print_rdma(ndev->rdma);
        dump_rx(ndev->ioaddr, ndev->rdma);
	}
	if(OPERATION_MODE & OPERATION_TX)
	{
		print_tdma(ndev->tdma);
		dump_tx(ndev->ioaddr, ndev->tdma);
	}
}